using System;
using System.ComponentModel;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Fitness;

namespace FilteredStatistics.Controller
{
    public class ActivityDataChangedHelper : INotifyPropertyChanged
    {
        public ActivityDataChangedHelper(IActivity activity)
        {
            Activity = activity;

            if (PluginMain.GetApplication() != null)
            {
                PluginMain.GetApplication().SystemPreferences.PropertyChanged += new PropertyChangedEventHandler(OnSystemPreferencesPropertyChanged);
                PluginMain.LogbookChanged += new PluginMain.LogbookChangedEventHandler(OnLogbookChanged);
                RegisterCategoryCallback(PluginMain.GetApplication().Logbook);
            }
        }

#region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

#endregion

        private void OnSystemPreferencesPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (Activity != null)
            {
                if (e.PropertyName.StartsWith("AnalysisSettings"))
                {
                    ActivityInfoCache.Instance.ClearInfo(m_Activity);

                    TriggerPropertyChangedEvent(PluginMain.GetApplication().SystemPreferences.AnalysisSettings, e.PropertyName);
                }
                else if (e.PropertyName == "DistanceUnits" ||
                         e.PropertyName == "ElevationUnits")
                {
                    if (m_Activity.Category.UseSystemLengthUnits)
                    {
                        TriggerPropertyChangedEvent(PluginMain.GetApplication().SystemPreferences, e.PropertyName);
                    }
                }
                else
                {
                    TriggerPropertyChangedEvent(PluginMain.GetApplication().SystemPreferences, e.PropertyName);
                }
            }
        }

        void OnLogbookChanged(object sender, ILogbook oldLogbook, ILogbook newLogbook)
        {
            Activity = null;

            UnregisterCategoryCallback(oldLogbook);
            RegisterCategoryCallback(newLogbook);
        }

        private void OnActivityDataChanged(object sender, PropertyChangedEventArgs e)
        {
            if (Activity != null)
            {
                ActivityInfoCache.Instance.ClearInfo(m_Activity);

                TriggerPropertyChangedEvent(m_Activity, "Activity." + e.PropertyName);
            }
        }

        void OnLapInfoPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (Activity != null)
            {
                TriggerPropertyChangedEvent(sender, "LapInfo." + e.PropertyName);
            }
        }

        private void OnActivityCategoryDataChanged(object sender, PropertyChangedEventArgs e)
        {
            if (Activity != null)
            {
                IActivityCategory modifiedCategory = sender as IActivityCategory;

                if (Utils.IsInActivityCategoryHierarchy(m_Activity, modifiedCategory))
                {
                    ActivityInfoCache.Instance.ClearInfo(m_Activity);

                    if (e.PropertyName != "StoppedMetersPerSecond" ||
                        !PluginMain.GetApplication().SystemPreferences.AnalysisSettings.IncludeStopped)
                    {
                        TriggerPropertyChangedEvent(m_Activity.Category, "ActivityCategory." + e.PropertyName);
                    }
                }

                if (e.PropertyName == "SubCategories")
                {
                    UnregisterCategoryCallback(PluginMain.GetApplication().Logbook);
                    RegisterCategoryCallback(PluginMain.GetApplication().Logbook);
                }

            }
        }

        private void RegisterCategoryCallback(ILogbook logbook)
        {
            if (logbook != null)
            {
                foreach (IActivityCategory category in logbook.ActivityCategories)
                {
                    RegisterCategoryCallback(category);
                }
            }
        }

        private void RegisterCategoryCallback(IActivityCategory category)
        {
            category.PropertyChanged += new PropertyChangedEventHandler(OnActivityCategoryDataChanged);

            foreach (IActivityCategory subCategory in category.SubCategories)
            {
                RegisterCategoryCallback(subCategory);
            }
        }

        private void UnregisterCategoryCallback(ILogbook logbook)
        {
            if (logbook != null)
            {
                foreach (IActivityCategory category in logbook.ActivityCategories)
                {
                    UnregisterCategoryCallback(category);
                }
            }
        }

        private void UnregisterCategoryCallback(IActivityCategory category)
        {
            category.PropertyChanged -= new PropertyChangedEventHandler(OnActivityCategoryDataChanged);

            foreach (IActivityCategory subCategory in category.SubCategories)
            {
                UnregisterCategoryCallback(subCategory);
            }
        }

        private void TriggerPropertyChangedEvent(object modifiedObject, string propertyName)
        {
            if (Activity != null && PropertyChanged != null)
            {
                PropertyChanged(modifiedObject, new PropertyChangedEventArgs(propertyName));

                // These criterias occur very often, so create a special even for them.
                //  Basically, they all change the activity's duration.
                if (propertyName == "AnalysisSettings.IncludePaused" ||
                    propertyName == "AnalysisSettings.IncludeStopped" ||
                    propertyName == "Activity.GPSRoute" ||
                    propertyName == "Activity.DistanceMetersTrack" ||
                    propertyName == "Activity.Category" ||
                    propertyName == "ActivityCategory.StoppedMetersPerSecond")
                {
                    PropertyChanged(Activity, new PropertyChangedEventArgs("Activity.Duration"));
                }
            }
        }

        public IActivity Activity
        {
            get { return m_Activity; }
            set
            {
                if (Activity != value)
                {
                    if (Activity != null)
                    {
                        Activity.PropertyChanged -= new PropertyChangedEventHandler(OnActivityDataChanged);

                        foreach (ILapInfo lapInfo in Activity.Laps)
                        {
                            lapInfo.PropertyChanged -= new PropertyChangedEventHandler(OnLapInfoPropertyChanged);
                        }
                    }

                    m_Activity = value;

                    if (Activity != null)
                    {
                        ActivityInfoCache.Instance.ClearInfo(m_Activity);
                        Activity.PropertyChanged += new PropertyChangedEventHandler(OnActivityDataChanged);

                        foreach (ILapInfo lapInfo in Activity.Laps)
                        {
                            lapInfo.PropertyChanged += new PropertyChangedEventHandler(OnLapInfoPropertyChanged);
                        }
                    }
                }
            }
        }

        private IActivity m_Activity = null;
    }
}
